package de.fhhannover.inform.itteam.snmpnetviz.gui;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

import javax.naming.ConfigurationException;

import de.fhhannover.inform.itteam.snmpnetviz.NetworkTreeFactory;
import de.fhhannover.inform.itteam.snmpnetviz.TreeConverter;
import de.fhhannover.inform.itteam.snmpnetviz.gui.renderer.SnmpNetVizLabelRenderer;
import de.fhhannover.inform.itteam.snmpnetviz.gui.renderer.SnmpNetzVizEdgeRenderer;

import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.controls.DragControl;
import prefuse.controls.FocusControl;
import prefuse.controls.PanControl;
import prefuse.controls.WheelZoomControl;
import prefuse.controls.ZoomToFitControl;
import prefuse.data.Tree;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.EdgeRenderer;
import prefuse.render.LabelRenderer;
import prefuse.render.Renderer;
import prefuse.util.ColorLib;
import prefuse.visual.VisualItem;

public class NetworkViewer {
	private NVFrame frame;
	// required properties (file)
	private static final String PROPERTIES = "snmpNetViz.prop";
	// configuration attributes
	private String switches;
	private int resX, resY;
	
	/*	------------------------  
			----------------
		------------------------  */
	
	private NetworkViewer() {	
		try {
			loadProperties();
		} catch (ConfigurationException ce) {
			ce.printStackTrace();
			System.exit(1);
		}
		
		Display d = configureVisualization();
		frame = startUserInterface(d);
	}
	
	/**
	 * 
	 * @throws ConfigurationException
	 */
	private void loadProperties() throws ConfigurationException {
		Properties props = new Properties();
		
		FileInputStream in;
		try {
			in = new FileInputStream(PROPERTIES);
		
			props.load(in);
			
			// retrieve each property
			switches = props.getProperty("switches");
			resX = Integer.parseInt(props.getProperty("res_x"));
			resY = Integer.parseInt(props.getProperty("res_y"));
			
			// checking against each used key
			if (switches == null || resX == 0 || resY == 0) {
				throw new ConfigurationException(">>	Properties configuration error!\n" +
						"  	(At least one required key wasn't found)");
			} 
			in.close();
		} catch (IOException ioe) {
			throw new ConfigurationException(ioe.getMessage());
		} catch (NumberFormatException nfe) {
			throw new ConfigurationException(">>	Properties configuration error!\n" + nfe.getMessage());
		}
	}
	
	/**
	 * 
	 */
	private Display configureVisualization() {		
		// -- 1. load the data ------------------------------------------------

		LoadingWindow loadingWindow = new LoadingWindow("Loading...");
		
		NetworkTreeFactory treefactory = new NetworkTreeFactory(switches,loadingWindow);
		Tree tree = TreeConverter.getTreeForHostEntryTree(treefactory.buildTree());
		
		loadingWindow.dispose();
		
		// -- 2. the visualization --------------------------------------------
        
        // add the graph to the visualization as the data group "graph"
        // nodes and edges are accessible as "graph.nodes" and "graph.edges"
		Visualization viz = new Visualization();
        viz.add("graph", tree);
        viz.setInteractive("graph.edges", null, false);
        
        // -- 3. the renderers and renderer factory ---------------------------
        
        // set own Renderer
        LabelRenderer lr= new SnmpNetVizLabelRenderer("name", "type");
        
        // set own EdgeRenderer
        Renderer er = new SnmpNetzVizEdgeRenderer();
        
		// create a new default renderer factory
        viz.setRendererFactory(new DefaultRendererFactory(lr, er));

        // -- 4. the processing actions ---------------------------------------
        
        // create our nominal color palette
        int[] palette = new int[] {
            ColorLib.rgb(255,000,000), ColorLib.rgb(000,000,255)
        };
        
        // map nominal data values to colors using our provided palette (based on vlan-affiliation)
        ColorAction text = new DataColorAction("graph.nodes", "vlan",
                Constants.NUMERICAL, VisualItem.TEXTCOLOR, palette);
        // use grey for edges
        ColorAction edges = new ColorAction("graph.edges",
                VisualItem.STROKECOLOR, ColorLib.gray(180));
        
        // create an action list containing all color assignments
        ActionList color = new ActionList();
//        color.add(fill);
        color.add(text);
        color.add(edges);
        
        // create an action list with an animated layout
        ActionList layout = new ActionList(ActionList.INFINITY);
        ForceDirectedLayout forceDirectedLayout = new SnmpForceDirectedLayout("graph", false);
		layout.add(forceDirectedLayout);
        layout.add(new RepaintAction());
        
        // add the actions to the visualization
        viz.putAction("color", color);
        viz.putAction("layout", layout);
        
        // -- 5. the display and interactive controls -------------------------
        Display display = new Display();
        display.setVisualization(viz);
        
        // drag individual items around
        display.addControlListener(new DragControl());
        // pan with left-click drag on background
        display.addControlListener(new PanControl()); 
        // zoom with right-click drag
        display.addControlListener(new ZoomToFitControl());
        display.addControlListener(new WheelZoomControl());
        
        display.setCustomToolTip(new javax.swing.JToolTip());
        display.addControlListener(new HoverToolTip());
        
        return display;
	}
	
	/**
	 * 
	 * @return
	 */
	private NVFrame startUserInterface(Display display) {
		NVFrame frame = new NVFrame(resX, resY, display);
		frame.setVisible(true);
		return frame; 
	}
	
	public static void main(String[] args) {
		new NetworkViewer();
	}

}
